using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Imagination.Collisions;

namespace Imagination.Partitioning
{
    /// <summary>
    /// BoxZone is a zone in the shape of an axis-aligned box. It can subdivide into 
    /// child BoxZones.
    /// </summary>
    public class BoxZone : Zone
    {
        private BoxZone parent = null;
        private BoxZone[] zones = null;

        private List<Thing> things = null;
        private AxisPartitioner overlaps = null;

        private bool isCosmos = false;   // true if represents all space
		private Vector3 min;
		private Vector3 max;	
        private BoundingBox boundary;

		public BoxZone Parent
		{
			get {return parent; }
		}

		public BoxZone[] Zones
		{
			get { return zones; }
		}

		public Vector3 Min
		{
			get { return min; }
		}

        public Vector3 Max
        {
            get { return max; }
        }

        public BoundingBox Boundary
        {
            get { return boundary; }
        }
        internal BoxZone() : base()
        {
            things = new List<Thing>();
            overlaps = new AxisPartitioner(this);
            isCosmos = true;
        }

        internal BoxZone(BoxZone parent, Vector3 min, Vector3 max) : base()
        {
            this.parent = parent;
            this.min = min;
            this.max = max;

            things = new List<Thing>();
            overlaps = new AxisPartitioner(this);

            this.boundary = new BoundingBox(min, max);
            isCosmos = false;
        }

        
        public override void ThingMoved(Thing thing)
        {
            BoxZone rootZone = this;
            while (rootZone.parent != null)
                rootZone = rootZone.parent;

            BoxZone newZone = rootZone.LocateZone(thing);
            if (newZone != thing.Zone)
            {
                thing.Zone.RemoveThing(thing);
                newZone.AddThing(thing);
                return;
            }
            overlaps.ThingMoved(thing);


            //if (Contains(thing) != ContainmentType.Contains)
            //{
            //    BoxZone rootZone = this;
            //    while (rootZone.parent != null)
            //        rootZone = rootZone.parent;

            //    BoxZone newZone = rootZone.LocateZone(thing);
            //    thing.Zone.RemoveThing(thing);
            //    newZone.AddThing(thing);
            //}
            //else
            //{
            //    if (((BoxZone)thing.Zone).zones != null)
            //    {
            //        BoxZone rootZone = this;
            //        while (rootZone.parent != null)
            //            rootZone = rootZone.parent;

            //        BoxZone newZone = rootZone.LocateZone(thing);
            //        thing.Zone.RemoveThing(thing);
            //        newZone.AddThing(thing);
            //        return;
            //    }
            //    overlaps.ThingMoved(thing);
            //}
        }

        public override void AddThing(Thing thing)
        {
            if (thing.Zone != null)
                thing.Zone.RemoveThing(thing);

            things.Add(thing);
            overlaps.AddThing(thing);

            thing.Zone = this;

            if (zones != null)
                foreach (BoxZone zone in zones)
                {
                    zone.PartitionAdd(thing);
                }
        }

        public override void RemoveThing(Thing thing)
        {
            if (thing.Zone == this)
            {
                things.Remove(thing);
                overlaps.RemoveThing(thing);

                thing.Zone = null;

                if (zones != null)
                    foreach (BoxZone zone in zones)
                    {
                        zone.PartitionRemove(thing);
                    }
            }
            else
            {
                throw new ImaginationException("Cannot remove object because it does not belong to this zone.");
            }
        }

        public override void ClearThings()
        {
            things.Clear();
            overlaps.ClearThings();
            if (zones != null)
                foreach (BoxZone zone in zones)
                    zone.ClearThings();
        }

        public override IEnumerable<Thing> EnumerateThings()
        {
            foreach (Thing thing in things)
                yield return thing;
        }

        internal void PartitionAdd(Thing thing)
        {
            overlaps.AddThing(thing);
            if (zones != null)
                foreach (BoxZone zone in zones)
                {
                    zone.PartitionAdd(thing);
                }
        }
        internal void PartitionRemove(Thing thing)
        {
            overlaps.RemoveThing(thing);
            if (zones != null)
                foreach (BoxZone zone in zones)
                {
                    zone.PartitionRemove(thing);
                }
        }

        public void SplitXAxis()
        {
            SplitXAxis(this.min, this.max);
        }

        public void SplitXAxis(Vector3 min, Vector3 max)
        {
            Vector3 center = new Vector3((min.X + max.X) / 2,
                (min.Y + max.Y) / 2, (min.Z + max.Z) / 2);

            zones = new BoxZone[2];

            //	zone A - near left
            zones[0] = new BoxZone(this, min, new Vector3(center.X, max.Y, max.Z));
            //	zone B - near right
            zones[1] = new BoxZone(this, new Vector3(center.X, min.Y, min.Z),
                max);
        }

        public void SplitZAxis()
        {
            SplitZAxis(this.min, this.max);
        }

        public void SplitZAxis4Way(Vector3 min, Vector3 max)
        {
            Vector3 center = new Vector3((min.X + max.X) / 2,
                (min.Y + max.Y) / 2, (min.Z + max.Z) / 2);

            Vector3 back = new Vector3((min.X + center.X) / 2,
                (min.Y + center.Y) / 2, (min.Z + center.Z) / 2);

            Vector3 front = new Vector3((center.X + max.X) / 2,
                (center.Y + max.Y) / 2, (center.Z + max.Z) / 2);

            zones = new BoxZone[4];

            //  back
            zones[0] = new BoxZone(this, min, new Vector3(max.X, max.Y, back.Z));
            //	mid-back
            zones[1] = new BoxZone(this, new Vector3(min.X, min.Y, back.Z),
                new Vector3(max.X, max.Y, center.Z));
            //	mid-front
            zones[2] = new BoxZone(this, new Vector3(min.X, min.Y, center.Z),
                new Vector3(max.X, max.Y, front.Z));
            //	front
            zones[3] = new BoxZone(this, new Vector3(min.X, min.Y, front.Z),
                max);
        }

        public void SplitZAxis(Vector3 min, Vector3 max)
        {
            Vector3 center = new Vector3((min.X + max.X) / 2,
                (min.Y + max.Y) / 2, (min.Z + max.Z) / 2);

            zones = new BoxZone[2];

            //	zone A - back
            zones[0] = new BoxZone(this, min, new Vector3(max.X, max.Y, center.Z));
            //	zone B - front
            zones[1] = new BoxZone(this, new Vector3(min.X, min.Y, center.Z),
                max);
        }

        public override ContainmentType Contains(Thing thing)
        {
            if (isCosmos) return ContainmentType.Contains;
            return boundary.Contains(thing.Boundary);
        }

        public BoxZone LocateZone(Thing thing)
        {
            if (Contains(thing) == ContainmentType.Contains)
            {
                if (zones == null) return this;

                foreach (BoxZone zone in zones)
                {
                    BoxZone result = zone.LocateZone(thing);
                    if (result != null)
                        return result;
                }
                return this;
            }
            return null;
        }

        public void CheckCollisions(CollisionDetector detector)
        {

            if (zones != null)
            {
                for (int i = 0; i < zones.Length; i++)
                {
                    zones[i].CheckCollisions(detector);
                }
            }
            else
            {
                overlaps.CheckCollisions(detector);
            }
        }

        public override string ToString()
        {
            string ret = things.Count.ToString();

            if (zones != null)
            {
                ret += " (";
                for (int i = 0; i < zones.Length; i++)
                    ret += zones[i].ToString() + (i != zones.Length - 1 ? ", " : "");
                ret += ")";
            }
            return ret;
        }
    }
}
